using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Criterion;
using System.Linq.Expressions;
using CNRegex.Enum;
using Project.Common;
using NHibernate.Type;
using NHibernate.Linq;

namespace CNRegex.Dal
{
	public class Repository<T> : IRepository<T> where T : class
	{
		private Type type = typeof(T);

		#region IRepository<T> Members

		public Repository()
		{
			
		}

		public virtual IList<T> FindByHQL(string hql)
		{
			IQuery query = Session.CreateQuery(hql);
			IList<T> list = query.Enumerable<T>().ToList();
			return list;
		}

		public virtual void Save(T entity)
		{
			Session.Save(entity);
		}

		public virtual void Update(T entity)
		{
			Session.Update(entity);
		}

		public virtual void SaveOrUpdate(T entity)
		{
			Session.SaveOrUpdate(entity);
		}

		public virtual void Delete(T entity)
		{
			Session.Delete(entity);
		}

		public virtual T Merge(T entity)
		{
			return (T)Session.Merge(entity);
		}

		public virtual void Persist(T entity)
		{
			Session.Persist(entity);
		}

		public virtual T GetByID<TInput>(TInput id)
		{
			IList<T> list = Session.CreateCriteria<T>().Add(Restrictions.Eq("ID", id)).List<T>();
			if (list == null || list.Count <= 0)
				return null;
			return list[0];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="id"></param>
		/// <param name="nullableType"></param>
		public virtual void DeleteByID<TInput>(TInput id, NullableType nullableType)
		{
			Session.Delete(string.Format("from {0} t where t.ID= ?", type.Name), id, nullableType);
		}

		public virtual IList<T> ListIn<U>(IList<U> collection, Expression<Func<T, object>> property)
		{
			MemberExpression me = property.Body as MemberExpression;
			string propertyName = "";
			if (me == null)
			{
				string body = property.Body.ToString();
				string strBody = body.Substring(8, body.Length - 8 - 1);
				propertyName = strBody.Substring(strBody.IndexOf(".") + 1, strBody.Length - strBody.IndexOf(".") - 1);
			}
			else
				propertyName = me.Member.Name;
			string strIds = string.Join(",", Array.ConvertAll<U, string>(collection.ToArray(), x => "'" + x.ToString() + "'"));
			IQuery query = Session.CreateQuery(string.Format("Select t from {0} t  where {2} in ({1})", NHibernateHelper.NHConguration.GetClassMapping(type).MappedClass.Name, strIds, propertyName));
			return query.List<T>();
		}


		public virtual void DeleteBy(DetachedCriteria criteria)
		{
			criteria.SetProjection(Projections.Property("ID"));
			Guid[] listGuids = criteria.GetExecutableCriteria(Session).List<Guid>().ToArray();
			string ids = string.Join(",", Array.ConvertAll<Guid, string>(listGuids, x => "'" + x.ToString() + "'"));
			ISQLQuery query = Session.CreateSQLQuery(string.Format("delete from {0} where id in ({1})", NHibernateHelper.NHConguration.GetClassMapping(type).Table.Name, ids));
			query.ExecuteUpdate();

		}

		public virtual IList<T> List(Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			IList<T> list = null;
			if (path == null)
				list = Session.Query<T>().ToList();
			else
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).ToList() : Session.Query<T>().OrderBy(path).ToList();
			return list;
		}

		public virtual IList<T> PaginateList(int? pageSize, int? pageIndex, ref int count, Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			IList<T> list = null;
			pageSize = pageSize ?? ProjectGlobal.PAGE_SIZE;
			if (path == null)
				list = Session.Query<T>().Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();
			else
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList() : Session.Query<T>().OrderBy(path).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();

			count = Session.Query<T>().Count();
			return list;
		}

		public virtual IList<T> PaginateListBy(int? pageSize, int? pageIndex, ref int count, Expression<Func<T, bool>> criteria, Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			IList<T> list = null;
			if (path != null && criteria != null)
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Where(criteria).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList() : Session.Query<T>().OrderBy(path).Where(criteria).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();
			else if (path == null && criteria != null)
				list = Session.Query<T>().Where(criteria).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();
			else if (path != null && criteria == null)
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList() : Session.Query<T>().OrderBy(path).Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();
			else
				list = Session.Query<T>().Skip((pageIndex ?? 0) * pageSize.Value).Take(pageSize.Value).ToList();

			count = criteria == null ? Session.Query<T>().Count() : Session.Query<T>().Where(criteria).Count();
			return list;
		}

		public virtual IList<T> ListBy(Expression<Func<T, bool>> criteria, Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			IList<T> list = null;
				if (path != null && criteria != null)
					list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Where(criteria).ToList() : Session.Query<T>().OrderBy(path).Where(criteria).ToList();
				else if (path == null && criteria != null)
					list = Session.Query<T>().Where(criteria).ToList();
				else if (path != null && criteria == null)
					list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).ToList() : Session.Query<T>().OrderBy(path).ToList();
				else
					list = Session.Query<T>().ToList();
			return list;
		}

		public virtual IList<T> ListTopX(Expression<Func<T, bool>> criteria, int count, Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			IList<T> list = null;
			if (path != null && criteria != null)
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Where(criteria).Take(count).ToList() : Session.Query<T>().OrderBy(path).Where(criteria).Take(count).ToList();
			else if (path == null && criteria != null)
				list = Session.Query<T>().Where(criteria).Take(count).ToList();
			else if (path != null && criteria == null)
				list = direction == EnumOrder.DESC ? Session.Query<T>().OrderByDescending(path).Take(count).ToList() : Session.Query<T>().OrderBy(path).Take(count).ToList();
			else
				list = Session.Query<T>().Take(count).ToList();
			return list;
		}

		public T GetFirst(Expression<Func<T, bool>> criteria, Expression<Func<T, object>> path = null, EnumOrder direction = EnumOrder.ASC)
		{
			var list = ListBy(criteria, path, direction);
			if (list.Count > 0)
				return list[0];
			return null;
		}

		#endregion

		public ISession Session
		{
			get
			{
				return NHibernateHelper.SessionFactory.GetCurrentSession();
			}
		}

		/// <summary>
		/// 向数据访问层类中的Dal类对象进行注入
		/// </summary>
		/// <param name="session"></param>
		public void Inject()
		{
			InjectDalObject.Inject(this);
		}
	}
}
